/*******************************************************************************
 * Copyright (c) 2000, 2011 QNX Software Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     QNX Software Systems - Initial API and implementation
 *     IBM Corporation
 *******************************************************************************/
package org.eclipse.cdt.ui.wizards.conversion;


import java.util.Vector;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableLayout;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;

import org.eclipse.cdt.core.CCProjectNature;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.CProjectNature;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.utils.ui.controls.ControlFactory;

import org.eclipse.cdt.internal.ui.CUIMessages;
import org.eclipse.cdt.internal.ui.ICHelpContextIds;
import org.eclipse.cdt.internal.ui.util.SWTUtil;


/**
 * <p>
 * ConvertProjectWizardPage  Standard main page for a wizard that converts a
 * project's nature.<br> This class provides the UI components and populates
 * the table with all  projects that meet the criteria specified by
 * subclasses in the method  isCandidate(IProject). This class does the
 * conversion through the method convertProjects([]Object), which is also
 * defined by all subclasses.<br> Subclasses provide the methods that
 * determine what files are displayed and what action is performed on them as
 * well as the labels for the Wizard.</p>
 *
 * Note: Only Projects that are open will be considered for conversion.
 *
 *
 * @author Judy N. Green
 * @since Aug 6, 2002 <p>
 */
public abstract class ConvertProjectWizardPage
    extends WizardPage {

	public static final String  KEY_TITLE = "ConvertionWizard.title"; //$NON-NLS-1$
    public static final String  KEY_CONVERTING = "ConvertionWizard.converting"; //$NON-NLS-1$
    private static final String PROJECT_LIST = "ConversionWizard.projectlist"; //$NON-NLS-1$

	protected boolean convertToC = false;
    protected boolean convertToCC = true;
    protected Button cRadioButton;
    protected Button ccRadioButton;

    // The Main widget containing the table and its list of candidate open projects
    protected CheckboxTableViewer tableViewer;

    protected Button selectAllButton;
    protected Button deselectAllButton;

    // We only need to calculate this once per instantiation of this wizard
    protected Object[] listItems = null;

    /**
     * Constructor for ConvertProjectWizardPage.
     *
     * @param pageName
     */
    public ConvertProjectWizardPage(String pageName) {
        super(pageName);
        setTitle(getWzTitleResource());
        setDescription(getWzDescriptionResource());
    }

    // get methods to allow values to be changed by subclasses
    protected abstract String getWzTitleResource();

    protected abstract String getWzDescriptionResource();

    /**
     * Returns the elements that the user has checked
     *
     * @return Object[]
     */
    protected Object[] getCheckedElements() {

        return tableViewer.getCheckedElements();
    }

    /**
     * Creates the main wizard page.
     *
     * @see org.eclipse.jface.dialogs.IDialogPage#createControl(Composite)
     */
    @Override
	public void createControl(Composite parent) {

        Composite  container = new Composite(parent, SWT.NONE);

        GridLayout layout = new GridLayout();
        layout.marginHeight = 0;
        layout.marginWidth = 0;
        container.setLayout(layout);
        setControl(createAvailableProjectsGroup(container));
        addToMainPage(container);
        // will default to false until a selection is made
        setPageComplete(validatePage());
        PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ICHelpContextIds.CONVERT_TO_CCPP_WIZARD_PAGE);
    }

    /**
     * Method addToMainPage allows subclasses to add
     * elements to the main page.
     *
     */
    protected void addToMainPage(Composite container){

		// Add convert to C or C/C++ buttons
		Composite area = ControlFactory.createGroup(container, CUIMessages.ConvertProjectWizardPage_convertTo, 2);


		SelectionListener cListener =  new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				convertToC = cRadioButton.getSelection();
				convertToCC = ccRadioButton.getSelection();
				validatePage();
			}
		};
		cRadioButton = ControlFactory.createRadioButton(area,
							  CUIMessages.ConvertProjectWizardPage_CProject,
							  "C ", //$NON-NLS-1$
			  			      cListener);
		cRadioButton.setSelection(convertToC);
		ccRadioButton = ControlFactory.createRadioButton(area,
							  CUIMessages.ConvertProjectWizardPage_CppProject,
							  "C++", //$NON-NLS-1$
			  			      cListener);
		ccRadioButton.setSelection(convertToCC);

		area.addDisposeListener(new DisposeListener() {
			@Override
			public void widgetDisposed(DisposeEvent event) {
				cRadioButton = null;
				ccRadioButton = null;
			}
		});
    }

    /**
     * Creates a list of projects that can be selected by the user.
     *
     * @param parent the parent composite
     * @return Composite
     */
    private final Composite createAvailableProjectsGroup(Composite parent) {

        // Add a label
        Label label = new Label(parent, SWT.LEFT);
        label.setText(CUIPlugin.getResourceString(PROJECT_LIST));

        Composite  container = new Composite(parent, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.marginHeight = 5;
        layout.marginWidth = 5;
        layout.numColumns = 2;
        container.setLayout(layout);
        GridData data = new GridData(GridData.FILL_BOTH);
        container.setLayoutData(data);

        // create the table
        Table    table = new Table(container,
                                   SWT.CHECK | SWT.BORDER | SWT.MULTI |
                                   SWT.SINGLE | SWT.H_SCROLL |
                                   SWT.V_SCROLL);
        data = new GridData(GridData.FILL_BOTH);
        table.setLayoutData(data);
        table.setHeaderVisible(true);
        table.setLinesVisible(false);
        table.getAccessible().addAccessibleListener(
            new AccessibleAdapter() {
                @Override
				public void getName(AccessibleEvent e) {
                        e.result = CUIPlugin.getResourceString(PROJECT_LIST);
                }
            }
        );

        TableLayout tableLayout = new TableLayout();
        table.setHeaderVisible(false);
        table.setLayout(tableLayout);

        // add a table viewer
        tableViewer = new CheckboxTableViewer(table);
        tableViewer.setLabelProvider(new ProjectLabelProvider());
        tableViewer.setContentProvider(new ProjectContentProvider());

        // set initial input
        tableViewer.setInput(getElements());

        // define and assign sorter
        tableViewer.setSorter(new ViewerSorter() {
            @Override
			public int compare(Viewer viewer, Object object1, Object object2) {

                if ((object1 instanceof IProject) && (object2 instanceof IProject)) {
                    IProject left = (IProject)object1;
                    IProject right = (IProject)object2;
                    int result = left.getName().compareToIgnoreCase(right.getName());

                    if (result != 0) {
                        return result;
                    }
                    return left.getName().compareToIgnoreCase(right.getName());
                }
                return super.compare(viewer, object1, object2);
            }

            @Override
			public boolean isSorterProperty(Object element, String property) {
                return true;
            }
        });
        tableViewer.setAllChecked(false);
        tableViewer.refresh();

        tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
			public void selectionChanged(SelectionChangedEvent e) {
                // will default to false until a selection is made
                setPageComplete(validatePage());
                updateSelectionButtons();
            }
        });
        // Add button panel

        Composite buttons= new Composite(container, SWT.NULL);
        buttons.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
        layout= new GridLayout();
        layout.marginHeight= 0;
        layout.marginWidth= 0;
        layout.verticalSpacing = 8;
        buttons.setLayout(layout);


        selectAllButton= new Button(buttons, SWT.PUSH);
        selectAllButton.setText(CUIMessages.ConvertProjectWizardPage_SelectAll);
        selectAllButton.setLayoutData(getButtonGridData(selectAllButton));
        selectAllButton.addListener(SWT.Selection, new Listener() {
            @Override
			public void handleEvent(Event e) {
                ConvertProjectWizardPage.this.tableViewer.setAllChecked(true);
                // update the pageComplete status
                setPageComplete(true);
                updateSelectionButtons();
            }
        });

        deselectAllButton= new Button(buttons, SWT.PUSH);
        deselectAllButton.setText(CUIMessages.ConvertProjectWizardPage_DeselectAll);
        deselectAllButton.setLayoutData(getButtonGridData(deselectAllButton));
        deselectAllButton.addListener(SWT.Selection, new Listener() {
            @Override
			public void handleEvent(Event e) {
                ConvertProjectWizardPage.this.tableViewer.setAllChecked(false);
                // update the pageComplete status
                setPageComplete(false);
                updateSelectionButtons();
            }
        });

        // enable or disable selection buttons
        Object[] elements = getElements();
        boolean enableSelectionButtons = (elements != null) && (elements.length > 0);

        selectAllButton.setEnabled(enableSelectionButtons);
        // we've called setAllChecked(false) earlier
        deselectAllButton.setEnabled(false);

        return parent;
    }

     /*
      * Method updateSelectionButtons, enables/disables buttons
      * dependent on what is selected
      */

     protected void updateSelectionButtons() {

        // update select and deselect buttons as required
        Object[] checkedObjects = getCheckedElements();
        int totalItems = tableViewer.getTable().getItemCount();
        boolean allSelected = checkedObjects.length == totalItems;
        boolean noneSelected = checkedObjects.length == 0;
        selectAllButton.setEnabled(!allSelected);
        deselectAllButton.setEnabled(!noneSelected);
      }
    /*
     * Method  getButtonGridData creates
     * and returns a GridData for the given button
     *
     * @GridData
     */
    private static GridData getButtonGridData(Button button) {
        GridData data= new GridData(GridData.FILL_HORIZONTAL);
        data.widthHint= SWTUtil.getButtonWidthHint(button);

        return data;
    }

    /**
     * Returns whether this page's controls currently all contain valid values.
     *
     * @return <code>true</code> if the user has selected at  least one
     *         candidate project.
     */
    protected boolean validatePage() {

        Object[] selection = getCheckedElements();

        return ((selection != null) && (selection.length > 0));
    }

    /**
     * Provides the contents for the list using the enclosing class's method
     * getElements();
     */
    public class ProjectContentProvider
        implements IStructuredContentProvider {
        @Override
		public Object[] getElements(Object parent) {
             return listItems;
        }

        @Override
		public void dispose() {}

        @Override
		public void inputChanged(Viewer viewer, Object oldInput,
                                 Object newInput) {}
    }

    /**
     * Provides labels for the listed items.  In this case it returns each
     * project's name
     */
    public class ProjectLabelProvider
        extends LabelProvider
        implements ITableLabelProvider {
        @Override
		public String getColumnText(Object obj, int index) {

            if (index == 0) {

                return ((IProject)obj).getName();
            }

            return ""; //$NON-NLS-1$
        }

        @Override
		public Image getColumnImage(Object obj, int index) {

            return PlatformUI.getWorkbench().getSharedImages().getImage(
                           IDE.SharedImages.IMG_OBJ_PROJECT);
        }
    }

    /**
     * Returns a list of open projects that are determined to be candidates
     * through the method isCandidate().<br>
     *
     * Note: Only Projects that are open will be considered for conversion.
     *
     * @return Object[] which may be null
     */
    protected Object[] getElements() {

        IWorkspace workspace = CUIPlugin.getWorkspace();
        IProject[] projects = workspace.getRoot().getProjects();
        Vector<IProject>     candidates = new Vector<IProject>(projects.length);
        IProject   next = null;

        // ensure we only present open, valid candidates to the user
        for (IProject project : projects) {
            next = project;

            if ((next != null)
                    && next.isOpen()
                        && isCandidate(next)) {
                candidates.addElement(next);
            }

            next = null;
        }

        // convert to an array for return
        Object[] candidateArray = null;

        if (candidates.size() > 0) {
            candidateArray = new Object[candidates.size()];
            candidates.copyInto(candidateArray);
        }
        // update the global variable that will
        // be returned by the ProjectContentProvider
        listItems = candidateArray;

        return candidateArray;
    }

    /**
     * doRun can be overwritten in subclasses to change behaviour, but this is
     * generally not required. It is called from the corresponding Conversion
     * Wizard
     *
     * @param monitor
     * @param projectID
     * @exception CoreException
     */
    public void doRun(IProgressMonitor monitor, String projectID) throws CoreException {

        Object[] selection = getCheckedElements();
        int      totalSelected = selection.length;

        if (totalSelected > 0) {
            if (monitor == null) {
                monitor = new NullProgressMonitor();
            }
            monitor.beginTask(CUIPlugin.getResourceString(KEY_TITLE), 1);
            convertProjects(selection, monitor, projectID);
        }
    }

    public void doRun(IProgressMonitor monitor, String projectID, String bsId) throws CoreException {
    	if(bsId == null)
    		doRun(monitor, projectID);
    	else {
	        Object[] selection = getCheckedElements();
	        if (selection != null) {
	        	int      totalSelected = selection.length;

	        	if (totalSelected > 0) {
	        		if (monitor == null) {
	        			monitor = new NullProgressMonitor();
	        		}
	        		monitor.beginTask(CUIPlugin.getResourceString(KEY_TITLE), 1);
	        		convertProjects(selection, bsId, monitor);
	        	}
	        }
    	}
    }

    /**
     * convertProjects calls the convertProject() method on each project
     * passed to it.
     *
     * @param selected
     * @param monitor
     * @param projectID
     * @throws CoreException
     */
    private void convertProjects(Object[] selected, IProgressMonitor monitor, String projectID)
                          throws CoreException {
        monitor.beginTask(CUIPlugin.getResourceString(KEY_CONVERTING),
                          selected.length);
		try {
	        for (Object element : selected) {
	            IProject project = (IProject)element;
    	        convertProject(project, new SubProgressMonitor(monitor, 1), projectID);
        	}
		} finally {
	        monitor.done();
		}
    }

    private void convertProjects(Object[] selected, String bsId, IProgressMonitor monitor)
    						throws CoreException {
		monitor.beginTask(CUIPlugin.getResourceString(KEY_CONVERTING),
		    selected.length);
		try {
			for (Object element : selected) {
			IProject project = (IProject)element;
			convertProject(project, bsId, new SubProgressMonitor(monitor, 1));
			}
		} finally {
			monitor.done();
		}
	}
    /**
     * Method finish we always finish successfully  :)
     *
     * @return boolean
     */
    public boolean finish() {

        return true;
    }

    /**
     * Must be overwritten in subclasses to change behaviour Determines which
     * projects will be displayed in the list
     *
     * @param project
     * @return boolean
     */
    public abstract boolean isCandidate(IProject project);

    /**
     * convertProject must be overwritten in subclasses to change behaviour
     *
     * @param project
     * @param monitor
     * @param projectID
     * @throws CoreException
     */
    public void convertProject(IProject project,
                                IProgressMonitor monitor,
                                String projectID)
                                throws CoreException{
        // Add the correct nature
    	if (convertToC) {
    		if (!project.hasNature(CProjectNature.C_NATURE_ID)){
    			addCNature(project, monitor, true);
    		} else {
    			if (project.hasNature(CCProjectNature.CC_NATURE_ID)){
    				// remove the C++ nature
    				CCProjectNature.removeCCNature(project, monitor);
    			}
    		}
    	} else {
    		if (convertToCC && !project.hasNature(CCProjectNature.CC_NATURE_ID)) {
    			addCCNature(project, monitor, true);
    		}
    	}
    }

    public void convertProject(IProject project,
    		String bsId,
            IProgressMonitor monitor)
            throws CoreException{
		// Add the correct nature
		if (convertToC) {
			if (!project.hasNature(CProjectNature.C_NATURE_ID)){
				addCNature(project, monitor, true);
			} else {
				if (project.hasNature(CCProjectNature.CC_NATURE_ID)){
						// remove the C++ nature
						CCProjectNature.removeCCNature(project, monitor);
				}
			}
		} else {
			if (convertToCC && !project.hasNature(CCProjectNature.CC_NATURE_ID)) {
				addCCNature(project, monitor, true);
			}
		}
	}

    protected void addCNature(IProject project, IProgressMonitor monitor, boolean addMakeBuilder) throws CoreException{
		if ( getWizard() instanceof ConversionWizard) {
			ConversionWizard cw = (ConversionWizard)getWizard();
			if(cw.getBuildSystemId() != null)
				CCorePlugin.getDefault().convertProjectToNewC(project, cw.getBuildSystemId(), monitor);
			else
				CCorePlugin.getDefault().convertProjectToC(project, monitor, cw.getProjectID());
		}
     }

     protected void addCCNature(IProject project, IProgressMonitor monitor, boolean addMakeBuilder) throws CoreException{
		if ( getWizard() instanceof ConversionWizard) {
	     	if (project.hasNature(CProjectNature.C_NATURE_ID)) {
		     	CCorePlugin.getDefault().convertProjectFromCtoCC(project, monitor);
     		} else {
     			ConversionWizard cw = (ConversionWizard)getWizard();
     			if(cw.getBuildSystemId() != null)
    	     		CCorePlugin.getDefault().convertProjectToNewCC(project, cw.getBuildSystemId(), monitor);
     			else
     				CCorePlugin.getDefault().convertProjectToCC(project, monitor, cw.getProjectID());
     		}
		}
     }

}
